package com.smarty.net.socket;

import com.smarty.net.layout.Transfer;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

@EqualsAndHashCode(callSuper = true)
@Slf4j
@Getter
@Setter
public abstract class UpStreamSocket extends Transfer {
    Transfer remoteTransfer;

    private ByteArrayOutputStream requestBody=new ByteArrayOutputStream();
    private Map<String,String> header=new HashMap<>();
    private String requestLine;
    private Integer parseIndex=0;


    public <T extends Transfer> UpStreamSocket(Socket selfSocket, T remoteTransfer) throws IOException {
        super(selfSocket);
        this.remoteTransfer =remoteTransfer;
    }
    public void self2Remote() throws IOException {
//        while (getBufferedInputStream().available()==0){
//            try {
//                Thread.sleep(0L);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//        }
        byte[] bytes=new byte[10240];
        int len;
        while ((len = readBytes(bytes))!=-1) {
            log.info("self2Remote ing");
            remoteTransfer.writeBytes(bytes,0,len);
        }
    }
    public void remote2Self() throws IOException {
//        while (remoteTransfer.getBufferedInputStream().available()==0){
//            try {
//                Thread.sleep(0L);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//        }
        byte[] bytes=new byte[10240];
        int len;
        while ((len = remoteTransfer.readBytes(bytes))!=-1) {
            log.info("remote2Self {}",new String(bytes,0,len));
            writeBytes(bytes,0,len);
        }
    }
    public void parse() throws IOException {
        byte[] byteArray = getRequestHeader().toByteArray();
        String line;
        int part=0;
        while ((line= readHeaderLine(byteArray,getParseIndex()))!=null){
            if(part==0){
                requestLine=line;
                part=1;
                continue;
            }
            if(part==1){
                if(line.equals("\r")){
                    part=3;
                    break;
                }else{
                    int i = line.indexOf(":");
                    if(i>0){
                        header.put(line.substring(0,i),line.substring(i+1));
                    }
                }
            }
        }
        if(part==3){
            String headerValue = getHeaderValue("Content-Length");
            if(Objects.nonNull(headerValue)){
                int i=getParseIndex();
                while (i<Integer.parseInt(headerValue)+getParseIndex()){
                    requestBody.write(byteArray[i]);
                    i++;
                }
            }
        }
        log.info("http parseEnd");
    }
    public String readHeaderLine(byte[] bytes,Integer parseIndex){
        int charPos = findCharPos(bytes, parseIndex, (byte) 0xa);
        if(charPos==-1){
            return null;
        }
        String s = new String(bytes, parseIndex, charPos - parseIndex);
        setParseIndex(charPos+1);
        return s;
    }
    public int findCharPos(byte[] bytes,int start,byte c){
        for (int i = start; i < bytes.length; i++) {
            if(bytes[i]==c){
                return i;
            }
        }
        return -1;
    }
    public String getHeaderValue(String headerName){
        Optional<Map.Entry<String, String>> first = header.entrySet().stream().filter(item -> item.getKey().equalsIgnoreCase(headerName)).findFirst();
        if(first.isPresent()){
            Map.Entry<String, String> stringStringEntry = first.get();
            return stringStringEntry.getValue().trim().replaceAll("\r$", "");
        }
        return null;
    }
}
